#include <QXmlAttributes>
#include <QXmlStreamWriter>
#include "DataTable.h"
#include "DataObject.h"
#include "scImpl.h"

#define FCT "DataObject_::DataObject_"
DataObject_::DataObject_() :
	dirty(false), touched(false)
{
	//ctrc(Trc::LOOP,APP,FCT)<<endl;
}

#undef FCT
#define FCT "DataObject::modifyData"
bool DataObject_::modifyData(const QXmlAttributes & attributes)
{
	// modifications of iks, iks2 and fk are already covered...
	bool changed(false);
	for(int i(1); i<attributes.count(); ++i) // _0 is pk!
	{
		//ctrc(Trc::LOOP,APP,FCT)<<"updating attribute "<<i<<" (total existing "<<size()<<")"<<endl;
		int idx(attributes.index(QString("_")+QString::number(i)));
		if(idx>=0)
			changed|=at(i).tryUpdate(attributes.value(idx), false);
	}
	dirty|=changed;
	return changed;
}

#undef FCT
#define FCT "DataObject_::toXml"
void DataObject_::toXml(QXmlStreamWriter * writer) const
{
	for(unsigned int i(0); i<size(); ++i)
	{
		writer->writeAttribute(QString("_")+QString::number(i), at(i).getAsQString(false));
	}
}

#undef FCT
#define FCT "DataObject::DataObject"
DataObject::DataObject(PkType primaryKey, const ColDescs & cds, const QXmlAttributes & attributes) :
	pk(primaryKey), ik(0), ikSort(0)
{
	bool doTrc(Trc::inst()->doTrace(Trc::LOOP,APP,FCT));
	pl.reserve(cds.size());
	// if((int)cds.size()!=attributes.count()) throw new AppException(FCT ": wrong number of attributes", true);
	// there may be additional attributes like "reason", so it is better to check each attribute
	for(int i(0); i<MAX_FK_PER_TABLE; ++i)
		fk[i]=0;
	int fki(0);
	// pk is already read and integer type
	pl.push_back(DataCell(DataCell::TY_DBKEY, ""));
	pl.back().getDbKeyV()=pk;
	if(doTrc)
		*(Trc::inst())<<Trc::baseInfo(APP,FCT).c_str()<<"created "<<pl.back().getDbKey()<<" with";
	for(unsigned int i(1); i<cds.size(); ++i)
	{
		switch(cds.at(i).dtype)
		{
		case ADTYPE_I32S:
			pl.push_back(DataCell(DataCell::TY_INT, ""));
			break;
		case ADTYPE_F64:
			pl.push_back(DataCell(DataCell::TY_DOUBLE, ""));
			break;
		case ADTYPE_STRASC:
		case ADTYPE_STRUTF8:
			//pl.push_back(DataCell(DataCell::TY_STRING, ""));
			pl.push_back(DataCell(DataCell::TY_QSTRING, ""));
			break;
		case ADTYPE_DBKEY:
			pl.push_back(DataCell(DataCell::TY_DBKEY, ""));
			break;
		case ADTYPE_DATIM:
			pl.push_back(DataCell(DataCell::TY_QDATETIME, ""));
			break;
		case ADTYPE_UNDEF:
		default:
			throw new AppException(FCT ": unknown dtype", true);
		}
		int aidx(attributes.index(QString("_")+QString::number(i)));
		if(aidx<0)
		{
			ostringstream os;
			os<<FCT<<": missing attribute _"<<i;
			throw new AppException(os.str().c_str(), true);
		}
		pl.back().clearDirty(); // try update may not move away from VIRGIN if the data is the same by pure chance!
		pl.back().tryUpdate(attributes.value(aidx), false);
		switch(cds.at(i).funct)
		{
		case AFUNCT_IK:
			ik=pl.back().getDbKey();
			if(doTrc)
				*(Trc::inst())<<" ik "<<ik;
			break;
		case AFUNCT_IKS:
			ikSort=pl.back().getAsInt();
			if(doTrc)
				*(Trc::inst())<<" iks "<<ikSort;
			break;
		case AFUNCT_IKS2:
			ikSort2=pl.back().getAsInt();
			if(doTrc)
				*(Trc::inst())<<" iks2 "<<ikSort2;
			break;
		case AFUNCT_FPK:
		case AFUNCT_FIK:
			fk[fki]=pl.back().getDbKey();
			if(doTrc)
				*(Trc::inst())<<" fk"<<fki+1<<" "<<fk[fki];
			++fki;
			break;
		case AFUNCT_DATA:
			if(doTrc)
				*(Trc::inst())<<" data "<<pl.back().getAsQString(false).toStdString().c_str();
			break;
		case AFUNCT_LUT:
			if(doTrc)
				*(Trc::inst())<<" lut "<<pl.back().getAsQString(false).toStdString().c_str();
			break;
		case AFUNCT_UNDEF: // ignore...
		case AFUNCT_PK: // already done
			if(doTrc)
				*(Trc::inst())<<" ??? ";
			break;
		}
	}
	if(doTrc)
		*(Trc::inst())<<endl;
}

#undef FCT
#define FCT "DataObject::~DataObject"
DataObject::~DataObject()
{
}

ostream& std::operator<< (ostream& out, const sybilpm::DataObjectModifier & dom)
{
	out<<dom.toString();
	return out;
}
